package com.bestv.search.engine.command;

import com.bestv.search.common.cache.SystemCache;
import com.bestv.search.common.dataSource.DataSourceKey;
import com.bestv.search.common.dataSource.DataSourceKeyUtil;
import com.bestv.search.common.model.*;
import com.bestv.search.common.service.InfoHeaderItemManager;
import com.bestv.search.common.service.InfoManager;
import com.bestv.search.common.service.NetInfoManager;
import com.bestv.search.common.util.CharUtil;
import com.bestv.search.common.util.ChineseCharToEn;
import com.bestv.search.common.util.StringConst;
import com.bestv.search.engine.compass.model.HdInfoLucene;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.SimpleDateFormat;
import java.util.*;

public class InfoCommand {
    private static SimpleDateFormat yearformat = new SimpleDateFormat("yyyy");
    protected final Log log = LogFactory.getLog(getClass());
    private InfoManager infoManager;
    private NetInfoManager netInfoManager;
    private TopNCacheCommand topNCacheCommand;
    private InfoHeaderItemManager infoHeaderItemManager;
    private SystemCache systemCache;

    public String topn(String type, boolean increase, int mergeType, int limit, String[] categorys) {
        Map<String, List<TopNCache>> topInfos = getTopNKeyword(type, increase, mergeType, categorys);

        // build XML to return
        StringBuffer result = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?><result>");
        if (topInfos != null && topInfos.size() > 0) {
            for (String key : topInfos.keySet()) {
                List<TopNCache> categoryTopNInfos = topInfos.get(key);
                int count = Math.min(limit, categoryTopNInfos == null ? 0 : categoryTopNInfos.size());
                result.append("<record>");
                result.append("<category>").append(key).append("</category>");
                result.append("<items>");
                for (int i = 0; i < count; i++) {
                    TopNCache cache = categoryTopNInfos.get(i);
                    result.append("<item>");
                    result.append("<name>").append(cache.getName()).append("</name>");
                    result.append("<index>").append(i).append("</index>");
                    result.append("<code>").append(cache.getCode() == null ? StringConst.EMPTY : cache.getCode())
                            .append("</code>");
                    result.append("<infoFlag>").append(cache.getInfoFlag() == null ? 0 : cache.getInfoFlag())
                            .append("</infoFlag>");
                    result.append("<count>").append(cache.getThisCount() == null ? 0 : cache.getThisCount())
                            .append("</count>");
                    result.append("</item>");
                }
                result.append("</items>");
                result.append("</record>");
            }
        }
        result.append("</result>");

        return result.toString();
    }

    public String topnJSON(String type, boolean increase, int mergeType, int limit, String[] categorys)
            throws JSONException {
        Map<String, List<TopNCache>> topInfos = getTopNKeyword(type, increase, mergeType, categorys);

        // build JSON String to return
        JSONArray array = new JSONArray();
        if (topInfos != null && topInfos.size() > 0) {
            for (String key : topInfos.keySet()) {
                List<TopNCache> categoryTopNInfos = topInfos.get(key);
                JSONObject category = new JSONObject();
                category.put("category", key);
                int count = Math.min(limit, categoryTopNInfos == null ? 0 : categoryTopNInfos.size());
                JSONArray items = new JSONArray();
                for (int i = 0; i < count; i++) {
                    TopNCache cache = categoryTopNInfos.get(i);
                    JSONObject item = new JSONObject();
                    item.put("name", cache.getName());
                    item.put("index", i);
                    item.put("code", cache.getCode() == null ? StringConst.EMPTY : cache.getCode());
                    item.put("infoFlag", cache.getInfoFlag() == null ? 0 : cache.getInfoFlag());
                    item.put("count", cache.getThisCount() == null ? 0 : cache.getThisCount());
                    item.put("category", cache.getCategory());
                    if (increase) {
                        item.put("increaseNo", cache.getIncreaseNo() == null ? 0 : cache.getIncreaseNo());
                    }
                    items.put(item);
                }
                category.put("items", items);
                array.put(category);
            }
        }
        return array.toString();
    }

    public String topnJSONToMgmt(String type, boolean increase, int mergeType, int limit, String[] categorys)
            throws JSONException {
        Map<String, List<TopNCache>> topInfos = getTopNKeyword(type, increase, mergeType, categorys);

        // build XML to return
        JSONArray array = new JSONArray();

        if (topInfos != null && topInfos.size() > 0) {
            for (String key : topInfos.keySet()) {
                List<TopNCache> categoryTopNInfos = topInfos.get(key);
                int count = Math.min(limit, categoryTopNInfos == null ? 0 : categoryTopNInfos.size());
                for (int i = 0; i < count; i++) {
                    TopNCache cache = categoryTopNInfos.get(i);
                    JSONObject item = new JSONObject();
                    item.put("name", cache.getName());
                    item.put("index", i + 1);
                    item.put("code", cache.getCode() == null ? StringConst.EMPTY : cache.getCode());
                    item.put("isHeader",
                            (cache.getInfoFlag() == null || cache.getInfoFlag() != 2) ? StringConst.NO_CHINESE
                                    : StringConst.YES_CHINESE);
                    item.put("count", cache.getThisCount() == null ? 0 : cache.getThisCount());
                    item.put("category", cache.getCategory());
                    if (increase) {
                        item.put("increaseNo", cache.getIncreaseNo());
                    }
                    array.put(item);
                }
            }
        }
        return array.toString();
    }

    public List<List<String>> topnToList(String type, boolean increase, int mergeType, int limit, String[] categorys) {
        List<List<String>> list = new ArrayList<List<String>>();

        Map<String, List<TopNCache>> topInfos = getTopNKeyword(type, increase, mergeType, categorys);
        if (topInfos != null && topInfos.size() > 0) {
            for (String key : topInfos.keySet()) {
                List<TopNCache> categoryTopNInfos = topInfos.get(key);
                int count = Math.min(limit, categoryTopNInfos == null ? 0 : categoryTopNInfos.size());
                for (int i = 0; i < count; i++) {
                    TopNCache cache = categoryTopNInfos.get(i);
                    List<String> row = new ArrayList<String>();
                    row.add(String.valueOf(i + 1));
                    row.add(cache.getName());
                    row.add(cache.getCode());
                    row.add(cache.getCategory());
                    row.add((cache.getInfoFlag() == null || cache.getInfoFlag() != 2) ? StringConst.NO_CHINESE
                            : StringConst.YES_CHINESE);
                    row.add(String.valueOf(cache.getThisCount()));
                    if (increase) {
                        row.add(String.valueOf(cache.getIncreaseNo()));
                    }

                    list.add(row);
                }
            }
        }
        return list;
    }

    public void removeOfflineCodesFromTopInfoCache(Set<String> offlineCodes) {
        // Removes all codes that have off lined from top infos caches
        topNCacheCommand.removeOfflineInfosFromMemoryCache(offlineCodes);
    }

    private Map<String, List<TopNCache>> getTopNKeyword(String type, final boolean increase, int mergeType,
                                                        String[] categorys) {
        Map<String, List<TopNCache>> results = new HashMap<String, List<TopNCache>>();
        if (StringConst.MERGE.equalsIgnoreCase(type)) {
            if (mergeType == 1) {
                return getMergeType1TopNInfos(increase, categorys);
            } else {
                log.error("Invalid merge type " + mergeType);
                return null;
            }
        } else {
            if (categorys == null || categorys.length == 0) {
                categorys = new String[]{StringConst.TOTAL_CHINESE};
            }
            for (String category : categorys) {
                List<TopNCache> list = topNCacheCommand
                        .getTopNListFromCache(StringConst.INFO, type, increase, category);
                results.put(category, list);
            }
            return results;
        }
    }

    private Map<String, List<TopNCache>> getMergeType1TopNInfos(boolean increase, String[] categorys) {
        String[] types = new String[]{StringConst.WEEKLY, StringConst.MONTHLY, StringConst.QUARTER};

        Map<String, List<TopNCache>> results = new HashMap<String, List<TopNCache>>();
        if (categorys == null || categorys.length == 0) {
            categorys = new String[]{StringConst.TOTAL_CHINESE};
        }
        // gets category's merge topNInfos
        for (String category : categorys) {
            Map<String, TopNCache> mergerdedCategorysTopNINfos = new HashMap<String, TopNCache>();
            for (String type : types) {
                // gets top infos from cache
                List<TopNCache> categorysTopNINfos = topNCacheCommand.getTopNListFromCache(StringConst.INFO, type,
                        increase, category);
                for (int i = 0; i < categorysTopNINfos.size(); i++) {
                    TopNCache topInfo = categorysTopNINfos.get(i);
                    double baseScore = 0;
                    if (StringConst.WEEKLY.equalsIgnoreCase(type)) {
                        baseScore = 0.2;
                    } else if (StringConst.MONTHLY.equalsIgnoreCase(type)) {
                        baseScore = 0.3;
                    } else {
                        baseScore = 0.5;
                    }
                    topInfo.setScore(baseScore * (i + 1));

                    TopNCache topNCacheInList = mergerdedCategorysTopNINfos.get(topInfo.getCode());
                    if (topNCacheInList != null) {
                        // replace topInfo by more smaller score's info
                        if (topNCacheInList.getScore() > topInfo.getScore()) {
                            mergerdedCategorysTopNINfos.put(topInfo.getCode(), topInfo);
                        }
                    } else {
                        mergerdedCategorysTopNINfos.put(topInfo.getCode(), topInfo);
                    }
                }
            }

            Collection<TopNCache> collection = mergerdedCategorysTopNINfos.values();
            List<TopNCache> list = new ArrayList<TopNCache>();
            for (TopNCache cache : collection) {
                list.add(cache);
            }

            // Sorce by score asc
            Collections.sort(list, new Comparator<TopNCache>() {
                @Override
                public int compare(TopNCache t1, TopNCache t2) {
                    if (t1.getScore() > t2.getScore()) {
                        return 1;
                    } else if (t1.getScore() < t2.getScore()) {
                        return -1;
                    } else {
                        return 0;
                    }
                }
            });

            results.put(category, list);
        }

        return results;
    }

    public int getOnLineInfosCount() {
        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        return infoManager.getOnLineInfosCount(key);
    }

    public List<Info> getOnLineInfos(int start, int limit) {
        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        return infoManager.getOnLineInfos(key, start, limit);
    }

    public List<Info> getInfosByUpdateTimeForIndex(Date updateIndexTimeForIndex) {
        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        return infoManager.getInfosByUpdateTimeForIndex(key, updateIndexTimeForIndex);
    }

    public Map<String, Integer> getMaxHitCountByTags(String p_type) {
        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        return infoManager.getMaxHitCountByTags(key, p_type);
    }

    public List<Info> convertToLuceneInfo(List<Info> list) {
        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        for (Info info : list) {

            // Merger netInfo with local info
            NetInfo netInfo = null;
            if (StringConst.MOVIE_CHINESE.equals(info.getP_type()) || StringConst.TV_CHINESE.equals(info.getP_type())) {
                netInfo = netInfoManager.getInfoByInfoId(key, info.getId());
            }

            if (netInfo != null) {
                // Genre
                if (netInfo.getGenre() != null) {
                    info.setGenre(netInfo.getGenre().replaceAll(StringConst.COMMA, StringConst.SPACE));
                }

                // Genre
                if (netInfo.getReleased() != null && netInfo.getReleased().length() > 4) {
                    info.setYear(netInfo.getReleased().substring(0, 4));
                }

                if (info instanceof Series) {
                    Series series = (Series) info;

                    // area
                    if (netInfo.getArea() != null) {
                        series.setRegion(netInfo.getArea().replaceAll(StringConst.COMMA, StringConst.SPACE));
                    }

                    // actors
                    if (netInfo.getPerformers() != null) {
                        series.setActors(netInfo.getPerformers().replaceAll(StringConst.COMMA, StringConst.SPACE));

                        String actorsStr = ChineseCharToEn.getAllFirstLetter(series.getActors());
                        series.setActorsPY(actorsStr + StringConst.SPACE + CharUtil.charToNum(actorsStr));
                    }

                    // directors
                    if (netInfo.getDirectors() != null) {
                        series.setDirectors(netInfo.getDirectors().replaceAll(StringConst.COMMA, StringConst.SPACE));

                        String directorsStr = ChineseCharToEn.getAllFirstLetter(series.getDirectors());
                        series.setDirectorsPY(directorsStr + StringConst.SPACE + CharUtil.charToNum(directorsStr));
                    }
                } else if (info instanceof Program) {
                    Program program = (Program) info;

                    // area
                    if (netInfo.getArea() != null) {
                        program.setRegion(netInfo.getArea().replaceAll(StringConst.COMMA, StringConst.SPACE));
                    }

                    // actors
                    if (netInfo.getPerformers() != null) {
                        program.setActors(netInfo.getPerformers().replaceAll(StringConst.COMMA, StringConst.SPACE));

                        String actorsStr = ChineseCharToEn.getAllFirstLetter(program.getActors());
                        program.setActorsPY(actorsStr + StringConst.SPACE + CharUtil.charToNum(actorsStr));
                    }

                    // directors
                    if (netInfo.getDirectors() != null) {
                        program.setDirectors(netInfo.getDirectors().replaceAll(StringConst.COMMA, StringConst.SPACE));

                        String directorsStr = ChineseCharToEn.getAllFirstLetter(program.getDirectors());
                        program.setDirectorsPY(directorsStr + StringConst.SPACE + CharUtil.charToNum(directorsStr));
                    }

                    // year
                    if (info.getYear() != null) {
                        Date date = program.getIssueYear();
                        if (date != null) {
                            program.setYear(yearformat.format(date));
                        }
                    }
                } else if (info instanceof Schedule) {
                    Schedule schedule = (Schedule) info;

                    // actors
                    if (netInfo.getPerformers() != null) {
                        schedule.setActors(netInfo.getPerformers().replaceAll(StringConst.COMMA, StringConst.SPACE));

                        String actorsStr = ChineseCharToEn.getAllFirstLetter(schedule.getActors());
                        schedule.setActorsPY(actorsStr + StringConst.SPACE + CharUtil.charToNum(actorsStr));
                    }

                    // directors
                    if (netInfo.getDirectors() != null) {
                        schedule.setDirectors(netInfo.getDirectors().replaceAll(StringConst.COMMA, StringConst.SPACE));

                        String directorsStr = ChineseCharToEn.getAllFirstLetter(schedule.getDirectors());
                        schedule.setDirectorsPY(directorsStr + StringConst.SPACE + CharUtil.charToNum(directorsStr));
                    }
                }
            }

            // Sets info header informations
            List<InfoHeaderItem> items = infoHeaderItemManager.getOnlineInfoHeaderItemByInfoId(key, info.getId());
            if (items != null && items.size() > 0) {
                info.setInfoFlag(1);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < items.size(); i++) {
                    sb.append(items.get(i).getInfo_header_id());
                    sb.append(StringConst.SPACE);
                    sb.append(items.get(i).getInfo_order());
                    if (i < items.size() - 1) {
                        sb.append(StringConst.SPACE);
                    }
                }
                info.setInfoHeaderItems(sb.toString());
            } else {
                info.setInfoFlag(0);
            }

            // For info record, infoCount always is 1.
            info.setInfoCount(1);
        }

        return list;
    }

    public List<HdInfoLucene> convertToHdInfoLucene(List<Info> infos) {
        final String docType = "vod";
        DataSourceKey key = DataSourceKeyUtil.convert(systemCache.getLocalDataSourceKey());
        List<HdInfoLucene> results = new ArrayList<HdInfoLucene>();
        for (Info info : infos) {
            NetInfo netInfo = null;
            if (StringConst.MOVIE_CHINESE.equals(info.getP_type()) || StringConst.TV_CHINESE.equals(info.getP_type())) {
                netInfo = netInfoManager.getInfoByInfoId(key, info.getId());
            }

            HdInfoLucene hdInfo = new HdInfoLucene();
            hdInfo.setId(docType + "-" + info.getId());

            // Set name
            String luceneName = info.getName();
            hdInfo.setName(luceneName);

            // Set name for sorting
            if (luceneName != null) {
                // replace un-used char, like '回看-', HD-, [纪实]
                String nameForSort = luceneName;
                if (nameForSort.startsWith("回看-")) {
                    nameForSort = nameForSort.replaceFirst("回看-", StringConst.EMPTY);
                }
                if (nameForSort.startsWith("HD-")) {
                    nameForSort = nameForSort.replaceFirst("HD-", StringConst.EMPTY);
                }
                if (nameForSort.startsWith("[纪实]")) {
                    nameForSort = nameForSort.replaceFirst("[纪实]", StringConst.EMPTY);
                }
                hdInfo.setNameForSort(nameForSort);
            }

            if (luceneName != null && luceneName.length() >= 1) {
                hdInfo.setNameFirstChar(luceneName.substring(0, 1));
            }

            // Set namePy
            String luceneNamePY = ChineseCharToEn.getAllFirstLetter(luceneName);
            hdInfo.setNamePY(luceneNamePY);
            if (luceneNamePY != null && luceneNamePY.length() >= 1) {
                hdInfo.setNamePYFirstChar(luceneNamePY.substring(0, 1));
            }

            hdInfo.setCode(info.getCode());
            hdInfo.setUpdateFlag(info.getUpdateFlag());
            hdInfo.setP_type(info.getP_type());
            hdInfo.setBizTypeLevel(info.getBizType().getLevel());
            hdInfo.setBizTypeValue(info.getBizType().getValue());
            if (info.getGenre() != null) {
                hdInfo.setGenre(info.getGenre().replaceAll(StringConst.SEMICOLON, StringConst.SPACE).trim());
            }

            Set<Tag> infoTags = info.getTags();
            if (infoTags != null) {
                StringBuilder sb = new StringBuilder();
                for (Tag tag : infoTags) {
                    sb.append(tag.getTag());
                    sb.append(StringConst.SPACE);
                }
                if (sb.length() > 0) {
                    hdInfo.setTags(sb.toString().trim());
                }
            }

            hdInfo.setCategory(info.getCategory());
            hdInfo.setLanguage(info.getLanguage());
            hdInfo.setHitCount(info.getHitCount());

            hdInfo.setHd(info.getHd());
            hdInfo.setIsCharge(info.getIsCharge());
            hdInfo.setType(info.getType() == null ? null : info.getType().getName());

            hdInfo.setUpdateTimeMillis(info.getUpdateTimeMillis());

            if (netInfo != null && netInfo.getReleased() != null) {
                // Get reased year
                try {
                    hdInfo.setYear(Integer.valueOf(netInfo.getReleased().substring(0, 4)));
                } catch (Exception e) {
                    log.warn("Get year failed from string " + netInfo.getReleased());
                    hdInfo.setYear(0);
                }
            } else {
                hdInfo.setYear(0);
            }

            // get all regions
            Set<String> regionSet = new HashSet<String>();
            if (netInfo != null && netInfo.getArea() != null) {
                String[] areas = netInfo.getArea().split(StringConst.COMMA);
                if (areas != null && areas.length > 0) {
                    for (String area : areas) {
                        if (StringConst.CHINA_CHINESE_MAIN.equals(area)) {
                            regionSet.add(StringConst.CHINA_CHINESE_INLAND);
                        } else {
                            regionSet.add(area);
                        }
                    }
                }
            }

            if (info instanceof Series) {
                Series s = (Series) info;
                if (s.getRegion() != null) {
                    String[] regionArray = s.getRegion().split(StringConst.SPACE);
                    if (regionArray != null && regionArray.length > 0) {
                        for (String region : regionArray) {
                            regionSet.add(region);
                        }
                    }
                }
                if (hdInfo.getYear() == 0) {
                    hdInfo.setYear(s.getIssueYear() == null ? 0 : Integer.valueOf(yearformat.format(s.getIssueYear())));
                }
            } else if (info instanceof Program) {
                Program s = (Program) info;
                if (s.getRegion() != null) {
                    String[] regionArray = s.getRegion().split(StringConst.SPACE);
                    if (regionArray != null && regionArray.length > 0) {
                        for (String region : regionArray) {
                            regionSet.add(region);
                        }
                    }
                }
                if (hdInfo.getYear() == 0) {
                    hdInfo.setYear(s.getIssueYear() == null ? 0 : Integer.valueOf(yearformat.format(s.getIssueYear())));
                }
            } else if (info instanceof Schedule) {
                Schedule s = (Schedule) info;
                hdInfo.setStartTime(s.getStartPlayTime());
                hdInfo.setEndTime(s.getEndPlayTime());
                hdInfo.setChannel(s.getChannel());
            }

            if (regionSet.size() > 0) {
                StringBuilder region = new StringBuilder();
                for (String r : regionSet) {
                    region.append(r);
                    region.append(StringConst.SPACE);
                }
                hdInfo.setRegion(region.toString().trim());
            }

            hdInfo.setContent_flag(info.getContent_flag());
            hdInfo.setContent_version(info.getContent_version());

            // Sets info header informations
            List<InfoHeaderItem> items = infoHeaderItemManager.getOnlineInfoHeaderItemByInfoId(key, info.getId());
            if (items != null && items.size() > 0) {
                hdInfo.setInfoFlag(1);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < items.size(); i++) {
                    sb.append(items.get(i).getInfo_header_id());
                    sb.append(StringConst.SPACE);
                    sb.append(items.get(i).getInfo_order());
                    if (i < items.size() - 1) {
                        sb.append(StringConst.SPACE);
                    }
                }
                hdInfo.setInfoHeaderItems(sb.toString());
            } else {
                hdInfo.setInfoFlag(0);
            }

            // For info record, infoCount always is 1.
            hdInfo.setInfoCount(1);

            hdInfo.setUp_flag(info.getUp_flag());

            results.add(hdInfo);
        }

        return results;
    }

    // Start getter/setter methods

    public InfoManager getInfoManager() {
        return infoManager;
    }

    public void setInfoManager(InfoManager infoManager) {
        this.infoManager = infoManager;
    }

    public SystemCache getSystemCache() {
        return systemCache;
    }

    public void setSystemCache(SystemCache systemCache) {
        this.systemCache = systemCache;
    }

    public NetInfoManager getNetInfoManager() {
        return netInfoManager;
    }

    public void setNetInfoManager(NetInfoManager netInfoManager) {
        this.netInfoManager = netInfoManager;
    }

    public TopNCacheCommand getTopNCacheCommand() {
        return topNCacheCommand;
    }

    public void setTopNCacheCommand(TopNCacheCommand topNCacheCommand) {
        this.topNCacheCommand = topNCacheCommand;
    }

    public InfoHeaderItemManager getInfoHeaderItemManager() {
        return infoHeaderItemManager;
    }

    public void setInfoHeaderItemManager(InfoHeaderItemManager infoHeaderItemManager) {
        this.infoHeaderItemManager = infoHeaderItemManager;
    }

    // End getter/setter methods
}